package com.tansun.ruledata.data.service.impl;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.tansun.rule.config.AppConfiguration;
import com.tansun.rule.pojo.EnterPoint;
import com.tansun.rule.utils.MClass;
import com.tansun.rule.utils.MField;
import com.tansun.rule.utils.StringUtils;
import com.tansun.ruledata.common.RuleConstants;
import com.tansun.ruledata.data.dao.BussinessSynchDAO;
import com.tansun.ruledata.data.entity.ProjectFlow;
import com.tansun.ruledata.data.entity.DeployRuleLogs;
import com.tansun.ruledata.data.entity.DeployDataSetParam;
import com.tansun.ruledata.data.entity.DeployDataset;
import com.tansun.ruledata.data.entity.DeployEnterpoint;
import com.tansun.ruledata.data.entity.DeployReferenceData;
import com.tansun.ruledata.data.entity.DeployVersionInfo;
import com.tansun.ruledata.data.entity.DeployDataparams;
import com.tansun.ruledata.data.service.BussinessSynchService;
import com.tansun.ruledata.data.service.ParamUtils;
import com.tansun.ruledata.entity.RuleDataInfo;
import com.tansun.ruledata.entity.RuleVersioninfo;
import com.tansun.ruledata.service.RuleDataCompileService;

@Service
public class BussinessSynchServiceImpl implements BussinessSynchService {
	
	private final Logger log = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private RuleDataCompileService ruleDataCompileService;
	
	@Autowired
	private BussinessSynchDAO  bussinessSynchDAO;
	
	@Autowired
	private  JdbcTemplate jdbcTemplate;
	
	@Override
	public List<DeployEnterpoint> getAllDeployInfo() {
		
		return bussinessSynchDAO.getAllDeployInfo();
	}

	
	@Override
	public String getDeploymentTimeStamp(String projectCode, String invokePoint) {		
	    List<String>  list  = this.bussinessSynchDAO.getDeploymentTimeStamp(ParamUtils.initMap(new String[] {"projectCode","invokePoint"}, new Object[] {projectCode,invokePoint} ));
		if (list != null &&list.size() >0) {
			return  list.get(0);
		}
		return null;
	}

	@Override
	public Map<String, Collection<DeployVersionInfo>> findDeployRuleVersionNew(String projectCode, String invokePoint) {

		return null;
	}
	
    private Map<String, DeployVersionInfo> findVersionByTimeStamp(String projectCode, String enterPointer,Map<String, DeployVersionInfo> vMap,String timeStamp) {
		final Map<String, DeployVersionInfo> conMap = vMap;
		Map<String,Object> paramMap = ParamUtils.initProjectEnterPoint(projectCode, enterPointer, timeStamp);
		List<DeployVersionInfo> list =this.bussinessSynchDAO.findVersion(paramMap);
		for (DeployVersionInfo ruleVersion : list) {
			if (ruleVersion.getModelCode() != null && !ruleVersion.getModelCode().startsWith("LS")) {
				ruleVersion.setDrlFile(ruleVersion.getId()+"_"+ruleVersion.getVersion());
				conMap.put(ruleVersion.getManageId()+ruleVersion.getVersion(), ruleVersion);
			}
		}	
		return conMap;
	}

	@Override
	public Map<String, Collection<DeployVersionInfo>> findDeployRuleVersionByTimestamp(String projectCode,
			String invokePoint, String timeStamp) {
		Map<String,Object>  paramMap  =  ParamUtils.initMap(new String[] {"projectCode","code","dataVersion"}, new Object[] {projectCode,invokePoint,timeStamp});
		List<ProjectFlow> list =this.bussinessSynchDAO.findDeployRuleVersionNew(paramMap);
		Map<String,Collection<DeployVersionInfo>>  vmap = new  HashMap<>();
		for (ProjectFlow projectInfo : list) {
			 Map<String,DeployVersionInfo>  childMap = this.findVersionByTimeStamp(projectCode,projectInfo.getCode(), new HashMap<String,DeployVersionInfo>(),timeStamp);
		     this.findFunctionInfoByTimeStamp(projectCode, childMap, timeStamp);
		     vmap.put(projectInfo.getCode(), childMap.values());
		}
		return vmap;
	}

	@Override
	public void findFunctionInfo(String projectCode, Map<String, DeployVersionInfo> conMap) {
		// TODO Auto-generated method stub

	}

	@Override
	public void findFunctionInfoByTimeStamp(String projectCode, Map<String, DeployVersionInfo> conMap, String timeStamp) {
		Map<String,Object> map  = new  HashMap<>();
		map.put("manageId", "func_"+projectCode);
		map.put("dataVersion", timeStamp);
		List<DeployVersionInfo> list =this.bussinessSynchDAO.findFunctionInfo(map);
		for (DeployVersionInfo  ruleVersion: list) {
			ruleVersion.setDrlFile(ruleVersion.getId()+"_"+ruleVersion.getVersion());
			conMap.put(ruleVersion.getManageId()+ruleVersion.getVersion(), ruleVersion);			
		}

	}

	@Override
	public Map<String, MClass> findRefParam(String projectCode, String enterPointer, Map<String, MClass> paramMap,
			Map<String, MClass> metaMap, String timeStamp) {
		Map<String,Object> map = new  HashMap<>();
		map.put("projectCode", projectCode);
		map.put("enterPointer", enterPointer);
		map.put("dataVersion", timeStamp);
		List<DeployDataSetParam> list =this.bussinessSynchDAO.findRefParam(map);
	    MClass   mClass = null;
	    MClass   tmpClass  = null;
		for (DeployDataSetParam ruleParam: list) {
	    	 String  className =ruleParam.getClassName();
	    	 String  nameEn =ruleParam.getNameEn();
	      	 mClass =paramMap.get(className);
	    	 tmpClass =metaMap.get(className);
	    	 if(tmpClass==null){
	    		 continue;
	    	 }
	    	 if (mClass == null){
	    		 mClass = new  MClass();
	    		 mClass.setName(tmpClass.getName());
	    		 mClass.setParamType(tmpClass.getParamType());
	    		 mClass.setChzn(tmpClass.getChzn());
	    		 mClass.setFieldMap(null);
	    		 mClass.setmFieldNames(null);
	    		 //mClass.getFieldMap().put(nameEn, tmpClass.getFieldMap().get(nameEn));
	    		 mClass.setParams(new ArrayList<MField>());
	    		 paramMap.put(className, mClass);
	    	 }
	    	 MField field = tmpClass.getFieldMap().get(nameEn);
	    	 MField newField = new MField();
	    	 try {
	    		 if(field==null)
	    			 continue;
				BeanUtils.copyProperties(field,newField);
 			 } catch (Exception e) {
				e.printStackTrace();
		 	 } 
	    	 newField.setParamFor(ruleParam.getContentType());
	    	 if(mClass.getFieldMap()==null) {
	    		 mClass.setFieldMap(new HashMap<String,MField>());
	    	 }
	    	 mClass.getFieldMap().put(newField.getName(), newField);
	    	mClass.getParams().add(newField);
		}
		return paramMap;
	}

	@Override
	public String ruleExecutionLogList(String projectCode) {
		Map<String,Object> param = new  HashMap<>();
		param.put("projectCode", projectCode);
		List<DeployRuleLogs> list =this.bussinessSynchDAO.ruleExecutionLogList(param);
		return JSON.toJSONString(list);
	}

	@Override
	public String findRulegroupEcharsDetails(String projectCode, String ruleFlowId) {
        Map<String,Object> map  = new  HashMap<>();
        map.put("projectCode", projectCode);
        map.put("ruleflowid", ruleFlowId);
        List<DeployRuleLogs> list=this.bussinessSynchDAO.findRulegroupEcharsDetails(map);
        return JSON.toJSONString(list);
	}

	public void synchRuleData(List<RuleDataInfo> ruledatainfoList,String projectCode) throws Exception {
 		String timeStamp = String.valueOf(new Date().getTime());
 		for(RuleDataInfo ruledatainfo:ruledatainfoList) {
 			depolyRuleData(ruledatainfo, projectCode, timeStamp);
 			reloadRuleData(ruledatainfo, projectCode, timeStamp);
 		}
 	}
 	
	public String depolyRuleData(RuleDataInfo ruledatainfo,String projectCode,String timeStamp) throws Exception {
		//RULE_DATASET RULE_DATAPARAMS
		String dsSql = "";
		List<Object[]> dsList = new ArrayList<Object[]>();
		for (DeployDataset ds : ruledatainfo.getDataSetlist()) {
			if(StringUtils.isEmpty(dsSql)) {
				dsSql = ds.getExeSql();
			}
			dsList.add(ds.getArgs(timeStamp));
		}
		if(!StringUtils.isEmpty(dsSql)) {
			jdbcTemplate.batchUpdate(dsSql, dsList);
		}
		String paramSql = "";
		List<Object[]> paramList = new ArrayList<Object[]>();
		for (DeployDataparams param : ruledatainfo.getParaList()) {
			if(StringUtils.isEmpty(paramSql)) {
				paramSql = param.getExeSql();
			}
			paramList.add(param.getArgs(timeStamp));
		}
		if(!StringUtils.isEmpty(paramSql)) {
			jdbcTemplate.batchUpdate(paramSql, paramList);
		}
		//rule_deployment rule_enterpoint
		jdbcTemplate.update(ruledatainfo.getDeployment().getExeSql(), ruledatainfo.getDeployment().getArgs(timeStamp));
		String enperPointers = "";
		String enterpointSql = "";
		List<Object[]> enterpointList = new ArrayList<Object[]>();
		for(DeployEnterpoint enterpoint : ruledatainfo.getEnterPoints()) {
			if(StringUtils.isEmpty(enterpointSql)) {
				enterpointSql = enterpoint.getExeSql();
			}
			enterpointList.add(enterpoint.getArgs(timeStamp));
			enperPointers += enterpoint.getCode()+";";
		}
		if(!StringUtils.isEmpty(enterpointSql)) {
			jdbcTemplate.batchUpdate(enterpointSql, enterpointList);
		}
		
		//RULE_VERSIONINFO 
		String versionSql = "";
		List<Object[]> versionList = new ArrayList<Object[]>();
		LobHandler lobHandler = new DefaultLobHandler();  // reusable object
		for(RuleVersioninfo ruleVersion : ruledatainfo.getFlowVersions()) {
			if(StringUtils.isEmpty(versionSql)) {
				versionSql = ruleVersion.getExeSql();
			}
			versionList.add(ruleVersion.getArgs(timeStamp));
			 jdbcTemplate.execute(versionSql,
			     new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
					@Override
					protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException {
						ps.setString(1,ruleVersion.getId());
						ps.setString(2,ruleVersion.getManageId());
						ps.setString(3,ruleVersion.getProjectCode());
						ps.setString(4,ruleVersion.getModelType());
						ps.setString(5,ruleVersion.getModelCode());
						ps.setString(6,ruleVersion.getVersion());
						String str = ruleVersion.getRuleContent().replace("\\'", "'");
						lobCreator.setClobAsString(ps, 7, str); //rule_content,
						ps.setString(8,ruleVersion.getEnterPointer());
						ps.setString(9,null);
						ps.setString(10,timeStamp);
						ps.setString(11,ruleVersion.getRefModelStatus());
					}
			     }
			 );
		}
//		jdbcTemplate.batchUpdate(versionSql, versionList);
		//RULE_REFERENCE
		String refSql = "";
		List<Object[]> refList = new ArrayList<Object[]>();
		if(ruledatainfo.getRefList()!=null){   //add by liupinghuai  如果没有引用变量的话会存在问题
			for(DeployReferenceData ref : ruledatainfo.getRefList()) {
				if(StringUtils.isEmpty(refSql)) {
					refSql = ref.getExeSql();
				}
				refList.add(ref.getArgs(timeStamp));
			}
			if(!StringUtils.isEmpty(refSql)) {
				jdbcTemplate.batchUpdate(refSql, refList);
			}	
		}
		return enperPointers;
	}
	@Override
	public Map<String, MClass> findMapping(String projectCode, String timeStamp) {
		Map<String,Object> map  = new  HashMap<>();
		map.put("projectCode", projectCode);
		map.put("dataVersion", timeStamp);
		List<DeployDataset> list =this.bussinessSynchDAO.findRuleDataSet(map);
		MClass mClass = null;
		Map<String, MClass> resultMap = new HashMap<String, MClass>();
		for (DeployDataset dataset :list) {
			 mClass = new MClass();
			 mClass.setName(dataset.getName());
			 mClass.setChzn(dataset.getChzn());
			 mClass.setParamType(dataset.getParamType());
			 mClass.setDesc(dataset.getDataDesc());
			 resultMap.put(mClass.getName(), mClass);
			 Map<String,Object> param  = new  HashMap<>();
			 param.put("datasetId", dataset.getId());
			 param.put("dataVersion", timeStamp);
			 List<DeployDataparams> dataList =this.bussinessSynchDAO.findRuleDataParams(param);
			 for (DeployDataparams dataParam : dataList) {
				 MField mField = new MField();
				 BeanUtils.copyProperties(dataParam, mField);
				 mClass.getmFieldNames().add(mField.getName());
				 mClass.getFieldMap().put(mField.getName(), mField);
			 }
			
		}
		return resultMap;
	}

	@Override
	public Map<String, EnterPoint> findEnterPoint(String projectCode, String pointCode, String timeStamp) {
		Map<String,Object> paramMap   = new  HashMap<>();
		paramMap.put("projectCode", projectCode);
		paramMap.put("code", pointCode);
		paramMap.put("dataVersion", timeStamp);
		List<EnterPoint> list =this.bussinessSynchDAO.findEnterPoint(paramMap);
		 Map<String,EnterPoint>  rMap  = new HashMap<>();
		 for (EnterPoint enterPoint:list) {
			 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			 enterPoint.setUpdateDate(dateFormat.format(new Date()));
			 rMap.put(enterPoint.getCode(), enterPoint);
			 
		 }
		return rMap;
	}

	@Override
	public String getEffectiveFunc(String projectCode, String timeStamp) {
		Map<String,Object> param = new HashMap<>();
  		String manageId = "func_"+projectCode;
		param.put("manageId", manageId);
		param.put("dataVersion", timeStamp);
		List<DeployVersionInfo> list =this.bussinessSynchDAO.findEffectiveFunc(param);
		if (list != null && list.size()>0) {
			return  list.get(0).getRuleContent();
		}
		return null;
	}

	@Override
	public Map<String, String> findRuleReleByProjectCode(String projectCode, String timeStamp) {
		Map<String,Object> param = new HashMap<>();
	  	final  Map<String,String>  retMap  = new  HashMap<>();
		param.put("projectCode", projectCode);
		param.put("dataVersion", timeStamp);
		List<DeployVersionInfo> list =this.bussinessSynchDAO.findRuleReleByProjectCode(param);
		for (DeployVersionInfo ruleVersion: list) {
			retMap.put(ruleVersion.getModelCode(), ruleVersion.getRuleContent());
		}
		return retMap;
	}

	@Override
	public Map<String, String> findPmmlByProjectCode(String projectCode, String timeStamp) {
		Map<String,Object> param = new HashMap<>();
	  	final  Map<String,String>  retMap  = new  HashMap<>();
		param.put("projectCode", projectCode);
		param.put("dataVersion", timeStamp);
		param.put("modelType", "34");
		List<DeployVersionInfo> list =this.bussinessSynchDAO.findRuleContentByInfo(param);
		for (DeployVersionInfo ruleVersion: list) {
			retMap.put(ruleVersion.getModelCode(), ruleVersion.getRuleContent());
		}		
		return retMap;
	}

	@Override
	public Map<String, Map<String, DeployReferenceData>> findPmmlRefByProjectCode(String projectCode, String timeStamp) {
    	Map<String,String> pmmlMap = AppConfiguration.PmmlMap.get(projectCode);
    	Map<String,Map<String, DeployReferenceData>> pmmlRefParamMap = new HashMap<>();
    	if(pmmlMap!=null) {
    		for(Entry<String, String> entry : pmmlMap.entrySet()) {
					Map<String,Object> param = new HashMap<>();
					param.put("projectCode", projectCode);
					param.put("dataVersion", timeStamp);
					param.put("modelCode", entry.getKey());
					List<DeployReferenceData> list =this.bussinessSynchDAO.findPmmlRefByProjectCode(param);
	        		if (list != null){
	        			Map<String,DeployReferenceData>  refMap = new HashMap<String, DeployReferenceData>();
	        			for (DeployReferenceData refData:list) {
	        				refMap.put(refData.getRefManageId(), refData);
	        			}
	        			pmmlRefParamMap.put(entry.getKey(), refMap);
	        		}	
    		}
    	}	
		return pmmlRefParamMap;
	}

	@Override
	public void findChallengerInfo(String projectCode, String enterpointer,boolean isTimestame) {

		Map<String,Object> map = new  HashMap<>();
		map.put("projectCode", projectCode);
		map.put("enterPointer", enterpointer);
		if (isTimestame) {
			String timestamp = getDeploymentTimeStamp(projectCode, enterpointer);
			map.put("dataVersion", timestamp);
		}
		
		List<DeployVersionInfo> list =this.bussinessSynchDAO.findChallengerInfo(map);
		if (list != null && list.size()>0) {
			 AppConfiguration.AppRuleProjChampionChallengerInfo.put(projectCode+"_"+enterpointer,list.get(0).getChampionChallengerInfo());
		}else {
			 AppConfiguration.AppRuleProjChampionChallengerInfo.put(projectCode+"_"+enterpointer, RuleConstants.championchallengerempty);
		}

	}
	
	/**
	 * 重载规则数据
	 */
	@Override
	public void reloadRuleData(RuleDataInfo ruledatainfo,String projectCode,String timstamp) {
		// 获取入口点
		List<DeployEnterpoint> enterPoints = ruledatainfo.getEnterPoints();
		List<String> enterList = new ArrayList<>();
		if(enterPoints != null && !enterPoints.isEmpty()) {
			enterPoints.forEach(item->{
				enterList.add(item.getCode());
			});
			String enterPoint = StringUtils.join(enterList,";");
			try {
				ruleDataCompileService.prepareDataToCacheRuleExe(null, projectCode, enterPoint);
				log.info("重载项目成功：" + projectCode + ",入口调用编码：" + enterPoint);
			} catch (Exception e) {
				log.error("重载项目异常：" + projectCode + ",入口调用编码：" + enterPoint);
				String error = ExceptionUtils.getStackTrace(e);
				log.error(error);
			}
		}
	}

}
